
#include <assert.h>

#include "utility/olisp_printer.h"
#include "utility/olisp_helpers.h"

static void print_list( list_obj_t * list, printer_context * ctx )
{
    size_t n;

    if( ctx->options & PRN_OPT_ADD_TYPEINFO )
        fputs( "LST:", ctx->stream );

    fputc( '(', ctx->stream );
    
    for( n = 0; n < list_length(list); ++n )
    {
        if( n > 0 )
            fputc( ' ', ctx->stream );

        print_elem( list_get_elem( list, n ), ctx );
    }
    
    fputc( ')', ctx->stream );
}

static void print_number( num_obj_t * num, printer_context * ctx )
{
    int add_typeinfo = ctx->options & PRN_OPT_ADD_TYPEINFO;
    
    if( add_typeinfo )
        fputs( "NUM", ctx->stream );

    if( num_is_double(num) )
    {
        if( add_typeinfo )
            fputs( "-DBL:", ctx->stream );

        fprintf( ctx->stream, "%f", num_as_double(num) );
    }
    else
    {
        if( add_typeinfo )
            fputs( "-INT:", ctx->stream );

        fprintf( ctx->stream, "%d", num_as_int(num) );
    }
}

#ifdef LISP_FEAT_QUOTE_SUPPORT
static void print_quote_symbol( obj_t * elem, printer_context * ctx )
{
    int quote_marker;
    if( !ctx->quote_mgr )
        return;
    
    quote_marker = quote_mgr_get_marker( ctx->quote_mgr, elem );
    switch( quote_marker )
    {
    case QUOTE_STRIGHT:
        fputc( '\'', ctx->stream );
        break;

    case QUOTE_BACK:
        fputc( '`', ctx->stream );
        break;

    case QUOTE_COMMA:
        fputc( ',', ctx->stream );
        break;

    case QUOTE_COMMA_AT:
        fputs( ",@", ctx->stream );
        break;
    }
}
#endif

void print_elem( obj_t * elem, printer_context * ctx )
{
#ifdef LISP_FEAT_QUOTE_SUPPORT
    print_quote_symbol( elem, ctx );
#endif

    switch( elem->vtbl->element_type )
    {
    case LISP_ELEM_ID:
        if( ctx->options & PRN_OPT_ADD_TYPEINFO )
            fputs( "ID:", ctx->stream );

        fputs( id_as_str((id_obj_t *)elem), ctx->stream );
        break;
        
    case LISP_ELEM_LIST:
        print_list( (list_obj_t *)elem, ctx );
        break;

    case LISP_ELEM_NUMBER:
        print_number( (num_obj_t *)elem, ctx );
        break;

    case LISP_ELEM_STRING:
        if( ctx->options & PRN_OPT_ADD_TYPEINFO )
            fputs( "STR:", ctx->stream );

        fputc( '"', ctx->stream );
        fputs( str_get_str((str_obj_t *)elem), ctx->stream );
        fputc( '"', ctx->stream );
        break;
        
    case LISP_ELEM_NIL:
        // it makes no sense to add type specifier to either `nil' or `t'
        fputs( "nil", ctx->stream );
        break;
        
    case LISP_ELEM_T:
        fputs( "t", ctx->stream );
        break;
        
    default:
        assert( !"can't work with such an element" );
        fputs( "?", ctx->stream );
    }
}
